#pragma once

#include <mutex>
#include <thread>
#include <functional>
#include <condition_variable>
#include <future>
#include <baseClass/GLog.h>
#include "SafeQueue.hpp"


struct Task
{
    std::string name = "unnamed";
    std::function<void()> task;
    

    Task():task([](){}){}
    Task(std::function<void()> task):task(task){};
    Task(std::string name, std::function<void()> task):name(name),task(task){};

    void operator()(){
        task();
    }
};


class ThreadPool
{
private:

    std::vector<std::thread> _threads;
    SafeQueue<Task> _queue;
    std::mutex _mutex;
    std::condition_variable _condition;
    
    bool _stop = false;

    class ThreadWorker
    {
    private:
        int id;
        ThreadPool *pool;
    public:
        ThreadWorker(int id, ThreadPool *pool): id(id), pool(pool){};
        void operator()()
        {
            Task task;

            while (!pool->_stop)
            {
                bool success = false;
                {
                    std::unique_lock<std::mutex> lock(pool->_mutex);
                    pool->_condition.wait(lock, [this]{ return pool->_stop||!pool->_queue.empty(); });
                    if(pool->_stop)
                        return;
                    task = pool->_queue.front();
                
                    success = pool->_queue.pop();
                }
                if(success)
                {
                    // ExecutionTimeLog(task,task.name);
                    task();
                }
            }
        }
    };

public:
    ThreadPool(int numThreads): _threads(std::vector<std::thread>(numThreads))
    {
        for (size_t i = 0; i < _threads.size(); ++i)
        {
            _threads.at(i) = std::thread(ThreadWorker(i,this)); // 分配工作线程
        } 
    }
    ThreadPool(const ThreadPool &) = delete;
    ThreadPool(ThreadPool &&) = delete;
    ThreadPool &operator=(const ThreadPool &) = delete;
    ThreadPool &operator=(ThreadPool &&) = delete;


    template <typename F, typename... Args>
    auto submit(std::string name,F &&f, Args &&...args)->std::future<decltype(f(args...))>
    {
       // Create a function with bounded parameter ready to execute
        std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...); 
        // 连接函数和参数定义，特殊函数类型，避免左右值错误
        // Encapsulate it into a shared pointer in order to be able to copy construct
        auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);  
        // Warp packaged task into void function
        std::function<void()> warpper_func = [task_ptr]()
        {
            (*task_ptr)();
        }; 
        // 队列通用安全封包函数，并压入安全队列
        Task task = {name, std::move(warpper_func)};
        _queue.push(task);
        // 唤醒一个等待中的线程
        _condition.notify_one();  
        // 返回先前注册的任务指针
        return task_ptr->get_future();
    }

    template <typename F, typename... Args>
    auto submit(F &&f, Args &&...args)->std::future<decltype(f(args...))>
    {
       // Create a function with bounded parameter ready to execute
        std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...); 
        // 连接函数和参数定义，特殊函数类型，避免左右值错误
        // Encapsulate it into a shared pointer in order to be able to copy construct
        auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);  
        // Warp packaged task into void function
        std::function<void()> warpper_func = [task_ptr]()
        {
            (*task_ptr)();
        }; 
        // 队列通用安全封包函数，并压入安全队列
        Task task;
        task.task = std::move(warpper_func);
        _queue.push(task);
        // 唤醒一个等待中的线程
        _condition.notify_one();  
        // 返回先前注册的任务指针
        return task_ptr->get_future();
    }


    int TaskSzie(){
        return _queue.size();
    }
    ~ThreadPool(){
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _stop = true;
        }
        _condition.notify_all();
        for (std::thread &t : _threads)
            t.join();
    };
};



